//--------------------------------------------------------------------------------
// This file is a portion of the D2D1Renderer.  It is distributed
// under the MIT License, available in the root of this distribution and 
// at the following URL:
//
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright (c) Mirco Timmermann
//
//--------------------------------------------------------------------------------
#include "Path.h"

#include <math.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif


Path::Path(ID2D1Factory* d2dFactory) {
	_d2dFactory = d2dFactory;

	_sink = 0;
	_geometry = 0;

	_begin = false;

	HRESULT hr = d2dFactory->CreatePathGeometry(&_geometry);
#ifdef _DEBUG
	if(FAILED(hr)) {
		//MessageBox(NULL, "Path::CreatePathGeometry failed", "Path", 0);
	}
#endif
}

Path::~Path(void) {
	if(_sink != 0) {
		_sink->Release();
		_sink = 0;
	}
	if(_geometry != 0) {
		_geometry->Release();
		_geometry = 0;
	}
}

void Path::Clear() {
	if(_sink != 0) {
		_sink->Release();
		_sink = 0;
	}
	if(_geometry != 0) {
		_geometry->Release();
		_geometry = 0;
	}

	HRESULT hr = _d2dFactory->CreatePathGeometry(&_geometry);
#ifdef _DEBUG
	if(FAILED(hr)) {
		//MessageBox(NULL, "Path::CreatePathGeometry failed", "Path", 0);
	}
#endif
}

/*
* Open a new geometry.
*/
void Path::Begin() {
	Clear();

	if(_geometry != 0) {
		HRESULT hr = _geometry->Open(&_sink);
#ifdef _DEBUG
		if(FAILED(hr)) {
			//MessageBox(NULL, "Begin::Open failed", "Path", 0);
		}
#endif
	}
}

/*
* Moves to a defined position. (MoveTo)
*/
void Path::Move(float x, float y) {
	if(_sink) {
		if(!_begin) { //if it has not begin -> let it begin.
			D2D1_POINT_2F point;
			point.x = x;
			point.y = y;
			_sink->BeginFigure(point, D2D1_FIGURE_BEGIN_FILLED); //D2D1_FIGURE_BEGIN_HOLLOW);

			_begin = true;
		} else { //if it has already begin -> close the current and make a new begin.
			_sink->EndFigure(D2D1_FIGURE_END_OPEN);
			_begin = false;

			Move(x, y);
		}
	}
}

/*
* Adds a new Line. (LineTo)
*/
void Path::Add(float x, float y) {
	if(!_begin) { //if there is no begin -> create one.
		Move(x, y); //0, 0
		return; //without
	}

	D2D1_POINT_2F point;
	point.x = x;
	point.y = y;

	if(_sink) _sink->AddLine(point);
}

/*
* Adds a new Bezier.
*/
void Path::AddBezier(float x1, float y1, float x2, float y2, float x3, float y3) {
	if(!_begin) { //if there is no begin -> create one.
		Move(x1, y1);
	}

	D2D1_POINT_2F point1;
	point1.x = x1;
	point1.y = y1;

	D2D1_POINT_2F point2;
	point2.x = x2;
	point2.y = y2;

	D2D1_POINT_2F point3;
	point3.x = x3;
	point3.y = y3;

	if(_sink) _sink->AddBezier(D2D1::BezierSegment(point1, point2, point3));
}

void Path::AddQuadraticBezier(float x1, float y1, float x2, float y2) {
	if(!_begin) { //if there is no begin -> create one.
		Move(x1, y1);
	}

	D2D1_POINT_2F point1;
	point1.x = x1;
	point1.y = y1;

	D2D1_POINT_2F point2;
	point2.x = x2;
	point2.y = y2;

	if(_sink) _sink->AddQuadraticBezier(D2D1::QuadraticBezierSegment(point1, point2));
}

void Path::AddArc(double x, double y, double radius, double startAngle, double endAngle, bool anticlockwise) {
	if (anticlockwise && startAngle < endAngle) {
		float oldStart = (float)startAngle;
		startAngle = endAngle;
		endAngle = oldStart;
	}

	if (endAngle - startAngle >= 2 * M_PI) {
		endAngle = float(startAngle + M_PI * 1.9999);
	} else if (startAngle - endAngle >= 2 * M_PI) {
		startAngle = float(endAngle + M_PI * 1.9999);
	}

	D2D1_POINT_2F startPoint;
	startPoint.x = (float)(x + radius * cos(startAngle));
	startPoint.y = (float)(y + radius * sin(startAngle));

	if(!_begin) { //if there is no begin -> create one.
		Move(startPoint.x, startPoint.y);
	}
	_sink->AddLine(startPoint);

	D2D1_POINT_2F endPoint;
	endPoint.x = (float)(x + radius * cos(endAngle));
	endPoint.y = (float)(y + radius * sin(endAngle));

	D2D1_ARC_SIZE arcSize = D2D1_ARC_SIZE_SMALL;

	if(anticlockwise) {
		if (startAngle - endAngle > M_PI) {
		  arcSize = D2D1_ARC_SIZE_LARGE;
		}
	} else {
		if (endAngle - startAngle > M_PI) {
		  arcSize = D2D1_ARC_SIZE_LARGE;
		}
	}

	_sink->AddArc(
		D2D1::ArcSegment(
			endPoint, 
			D2D1::SizeF((float)radius, (float)radius),
            0.0f,
            anticlockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE : D2D1_SWEEP_DIRECTION_CLOCKWISE,
            arcSize
		)
	);
}

/*
* close the path (start point will be connected with the end point)
*/
void Path::close() {
	if (_sink) {
		_sink->EndFigure(D2D1_FIGURE_END_CLOSED);
		
		_sink->Close();
		_sink->Release();
		_sink = NULL;
		_begin = false;
	}
}

/*
* close the path (with open end point)
*/
void Path::end() {
	if (_sink) {
		_sink->EndFigure(D2D1_FIGURE_END_OPEN);

		_sink->Close();
		_sink->Release();
		_sink = NULL;
		_begin = false;
	}
}

ID2D1PathGeometry* Path::GetGeometry() {
	return _geometry;
}